#ifndef VECTOR3_H_
#define VECTOR3_H_

#include "Fixed.h"
#include "Matrix.h"

namespace uav
{
namespace math
{

/**
 * @class Vector3
 *
 * @brief Three-dimensional fixed-point vector class.
 *
 * All vectors are treated as column vectors when dealing with matrices.
 *
 * @author Travis Portz
 */
class Vector3
{
public:
	Fixed x; ///< The @a x-component of the vector.
	Fixed y; ///< The @a y-component of the vector.
	Fixed z; ///< The @a z-component of the vector.

	/**
	 * Constructs a vector initialized to the zero vector.
	 */
	Vector3() {}

	/**
	 * Constructs a vector with the given components.
	 */
	Vector3(const Fixed &x, const Fixed &y, const Fixed &z) : x(x), y(y), z(z) {}

	/**
	 * Computes the length of the vector squared.
	 *
	 * This method is faster than computing the length and then squaring it.
	 * No square root operation is used.
	 */
	Fixed lengthSquared() const { return x * x + y * y + z * z; }

	/**
	 * Computes the length of the vector.
	 */
	Fixed length() const { return sqrt(lengthSquared()); }

	/**
	 * Normalizes the length of this vector to one.
	 */
	void normalize()
	{
		Fixed s = length();
		if (s == Fixed::Zero) return;
		s = Fixed::One / s;
		x *= s; y *= s; z *= s;
	}

	/**
	 * Returns a new vector with length one and direction equal to the direction of this vector.
	 *
	 * This method does not modify the original vector.
	 *
	 * @see normalize()
	 */
	Vector3 getNormal() const
	{
		Vector3 temp = *this;
		temp.normalize();
		return temp;
	}

	/**
	 * Computes the dot product between this vector and @a v.
	 */
	Fixed dot(const Vector3 &v) const { return x * v.x + y * v.y + z * v.z; }

	/**
	 * Computes the cross product of this vector and @a v.
	 */
	Vector3 cross(const Vector3 &v) const
	{
		return Vector3(
			y * v.z - z * v.y,
			z * v.x - x * v.z,
			x * v.y - y * v.x
		);
	}

	/**
	 * Tranform vector @a v by the matrix @a m (assuming @a v is a column vector).
	 *
	 * @return The transformed vector.
	 */
	static Vector3 transform(const Matrix &m, const Vector3 &v);

	/**
	 * Transform vector @a v by the transpose of matrix @a m (assuming @a v is a column vector).
	 *
	 * If @a m is a rotation matrix, this is equivalent to transforming @a v by the inverse of @a m.
	 *
	 * @return The transformed vector.
	 */
	static Vector3 transformTranspose(const Vector3 &v, const Matrix &m);

	static const Vector3 UnitX; ///< The unit vector in the x-axis.
	static const Vector3 UnitY; ///< The unit vector in the y-axis.
	static const Vector3 UnitZ; ///< The unit vector in the z-axis.
	static const Vector3 Zero;  ///< The zero vector.

	Vector3& operator+=(const Vector3 &v)
	{
		x += v.x; y += v.y; z += v.z;
		return *this;
	}
	Vector3& operator-=(const Vector3 &v)
	{
		x -= v.x; y -= v.y; z -= v.z;
		return *this;
	}
	Vector3& operator*=(const Fixed &s)
	{
		x *= s; y *= s; z *= s;
		return *this;
	}
	Vector3& operator/=(const Fixed &s)
	{
		x /= s; y /= s; z /= s;
		return *this;
	}

	Vector3 operator+(const Vector3 &v) const
	{
		Vector3 temp = *this;
		temp += v;
		return temp;
	}
	Vector3 operator-(const Vector3 &v) const
	{
		Vector3 temp = *this;
		temp -= v;
		return temp;
	}
	Vector3 operator*(const Fixed &s) const
	{
		Vector3 temp = *this;
		temp *= s;
		return temp;
	}
	Vector3 operator/(const Fixed &s) const
	{
		Vector3 temp = *this;
		temp /= s;
		return temp;
	}

	Vector3 operator-() const
	{
		return Vector3(-x, -y, -z);
	}

	friend Vector3 operator*(const Fixed &s, const Vector3 &v)
	{
		Vector3 temp = v;
		temp *= s;
		return temp;
	}
};

}}

#endif
